/**
 * SPDX-FileCopyrightText: 2023-2025 Sangfor Technologies Inc.
 * SPDX-License-Identifier: Mulan PSL v2
 */
<template>
    <div class="feature-manager-wrap">
        <flow-common-top @roll-back="refreshAIFlow" />
        <div class="feature-manager-container">
            <div class="operate-step__wrap">
                <div class="operate-step__left">
                    <page-title
                        :title-name="_('特征处理')"
                        :small-size="true"
                        :border-none="true"
                        class="operate-title" />
                </div>
                <div v-if="showEmptyStep"
                     class="empty-step__container"
                     :class="{'has-desource':showDesourceOperate}">
                    <div class="content">
                        <lang>由上至下依次新增</lang>
                    </div>
                    <div class="content">
                        <lang>特征处理步骤</lang>
                    </div>
                    <img src="../img/empty-step.png"
                         class="empty-img"
                         alt="">
                </div>
                <operate-step
                    v-else
                    :step-list="ruleGraphList"
                    :active-index="operateActiveIndex"
                    @operate-preview="queryOperatePreview"
                    @operate-delete="_deleteOperate"
                    @operate-edit="_editOperateStep"
                    @all-step-delete="_deleteAllStep" />
                <div class="operata-btn">
                    <sf-button-primary
                        :default-disabled="addStepsStatus"
                        class="common-btn add-btn"
                        @click="_addOperateStep">
                        <lang>新增特征处理步骤</lang>
                    </sf-button-primary>
                </div>
            </div>
            <div class="preview-operate">
                <empty-panel v-if="ruleGraphList.length === 0"
                             :text="emptyText" />
                <!-- <empty-preview
                    v-show="ruleGraphList.length === 0" /> -->
                <!-- 数据源名称 -->
                <page-title :title-name="dataSourceTitle"
                            :small-size="true"
                            class="preview-operate__top-title" />
                <sf-toolbar v-if="ruleGraphList.length > 0"
                            ref="toolbar">
                    <sf-button
                        ref="submit"
                        class="creat-button btn-blank"
                        icon-cls="iconpiechart"
                        :default-disabled="showEmptyStep"
                        @click="_featureVisual">
                        <lang>特征可视化</lang>
                    </sf-button>
                </sf-toolbar>
                <preview-table
                    ref="previewTable"
                    :height-flag="!showDataSourceActive"
                    :table-header-list="tableHeaderList"
                    :table-data="previewList" />
                <!-- 无预览结果 -->
            </div>
        </div>
    </div>
</template>
<script>

import { encodeDate } from 'src/util/format';

import store from 'src/vuex/store';
let userID = store.state.userId;

import { deepClone } from 'src/util/deep_clone';

import OperateStep from './operate_step/index.vue';
import addOperateStepForm from './label_form/add_operate_step.vue';

import PreviewTable from './component/preview_table.vue';
import pageTitle from 'src/home/mod_cluster/common/page_title/page_title';

// import EmptyPreview from './component/empty_preview.vue';
import urlAdapter from 'src/home/mod_common/url_adapter';

import parseJson from 'src/util/parse_json.ts';

import trainingUrlAdapter from 'src/home/mod_common/training_url_adapter';

const FLOW_URL = {
    post: trainingUrlAdapter('flow'),
    put:trainingUrlAdapter('flow'),
    get:trainingUrlAdapter('flow')
};

// import moment from 'moment';
import TRAINING_INFO from '../const.ts';


// 特征可视化
import FeatureVisual from './component/feature_visual.vue';


import FlowCommonTop from '../components/flow_common_top.vue';
import EmptyPanel from 'src/components/empty_panel.vue';
import FlowOpr from '../components/common/flow_opr.js';

export default {
    name: 'FeatureManager',
    components: {
        OperateStep,
        PreviewTable,
        pageTitle,

        // EmptyPreview,
        FlowCommonTop,
        EmptyPanel
    },
    mixins:[FlowOpr],
    data () {
        return {
            emptyText:_('您还未导入数据源，请先到【数据加载】导入数据集。'),
            operatorDetailData:{},
            operateList:[],
            ruleGraphList:[], // 图列表
            ruleGraphStepList:[], // 步骤列表
            showDataSourceActive:false,
            previewList:[], // 预览列表
            tableHeaderList:[], // 预览列表表头
            operateActiveIndex:-1, // 步骤activeIndex
            fieldList:[], // 字段列表
            timeFieldList:[], // 时间字段列表
            operateLayer: null,
            dataSourceId:'', // es数据源id
            indexName:'', // es索引
            oprIndex:-1, // 编辑算子所用
            graphId:'',
            graphMangerList:[],
            groupByKeys:'[]', // 分组字段，给的平滑和数据处理用
            trainAllData:[], // 需要选择标签或训练的总数据
            showAnalysisActive:true, // 默认肯定不可以点击
            dataSourceTitle:'', // 数据源title
            dataSourceName:'', // 数据源名称
            dataSourceType:'', // 数据源类型
            editFlowName:'',
            graphRule:{},
            columnsList:[],  // 特征探索最后步骤的字段
            allColumns:[],  // 所有表头字段
            nullAIInfo:false,
            changeFeatureStepFlag:false, // 是否有修改特征探索步骤。
            firstColumns:{}, // 代表全部字符串，后台需要
            lastColumns:{}, // 最后所有字段
            secondLatestDataTypes:{}, // 倒数第二步所有字段列表
            originalColumns:[],  // 第一次返回数据源的字段
            originalColumnsObj:{}
        };
    },
    computed: {
        projectId () {
            return this.$route.query.projectId || '';
        },

        // //列表第1个肯定为数据源算子，如果只存在数据源算子的话，步骤也需显示
        showEmptyStep () {
            return this.ruleGraphList.length <= 1;
        },
        showDesourceOperate () {
            return this.ruleGraphList.length > 0;
        },
        addStepsStatus () {

            // 预览数据为空或没有添加数据集时都为true
            return this.ruleGraphList.length === 0 || this.previewList.length === 0;
        },
        addGenerateStatus () {

            // 有步骤而且有预览数据的时候为false
            let statusFlag = this.ruleGraphList.length > 1 && this.previewList.length > 0;
            return !statusFlag;
        },

        // 最后一个算子步骤
        lastGraphIndex () {
            return this.ruleGraphList.length - 1;
        },

        //
        getDataSourceParams () {
            let params = this.ruleGraphList.length ? parseJson(this.ruleGraphList[0].jsonValue) : {};
            return params;
        },
        aiFlowSnapshotId () {
            return this.$route.query.aiFlowSnapshotId || '';
        },
        aiFlowId () {
            return this.$route.query.aiFlowId || '';
        }

    },
    mounted () {
        if (this.isNullObject(this.aiFlowInfo)) {
            this._getAIFlowSnapShot();
        } else {
            this.handleAIFlowInfo(this.aiFlowInfo);
        }
    },
    beforeRouteLeave (to, from, next) {
        if (to.name === 'data_load' || to.name === 'model_training') {
            next(true);
            return false;
        };
        if (this.nullAIInfo) {
            this.saveAiFlowInfo({});
            next(true);
        } else {

             // 数据源添加后，保存了aiflowname名称，但此时没有编辑ai_flow名称，因为已经保存了，所以不弹窗。
            this.$confirm({
                title: _('提示'),
                msg: _('AI FLOW 尚未保存，是否离开当前页面？'),
                autoDestroy: true,
                autoClose: true,
                buttons: [{
                    cls: 'visual-save-exit-btn',
                    actionName: 'leave',
                    loading: true,
                    text:  _('直接离开')
                }, {
                    actionName: 'submit',
                    loading: true,
                    text:_('保存并离开')
                }, {
                    actionName: 'cancel',
                    text: '取消'
                }],
                leave: () => {
                    this.saveAiFlowInfo({});
                    next(true);
                },
                submit: () => {
                    this._saveGraph().then(() => {
                        this.saveAiFlowInfo({});
                        next(true);
                    });
                }
            });
        }

    },
    methods: {
        refreshAIFlow (data) {
            this.changeFeatureStepFlag = false;
            this.saveAiFlowInfo(data);
            this.handleAIFlowInfo(data);
        },
        _createOpeateStepFormWin () {
            this.operateStepFormWin = this.$modal(addOperateStepForm, {
                autoDestory: true
            });
        },

        // 新增模型训练
        _addOperateStep (e) {
            this._createOpeateStepFormWin();

            // 新增时，取最后一个list的column为字段列表
            let fieldList = this.getTransformFieldList(this.lastColumns);
            this._createAddOperateLayer();
            let normalData = {
                fieldList,
                timeFieldList: this.originalColumns,
                groupByKeys: this.groupByKeys // 分组和时序处理算子需要用
            };
            this.addOperateLayer.update(normalData).showTo(e.target);
        },
        _createAddOperateLayer () {
            this.addOperateLayer && this.addOperateLayer.destroy();
            this.addOperateLayer = this.$layer(addOperateStepForm, {
                anchor: 'right-end',
                cls: 'operate__layer',
                autoHide:true,
                'arrow-hide':true
            });

            let vm = this;
                let operateFormRoot = this.addOperateLayer.formRoot;

            // 新增算子成功
            operateFormRoot.$on('operate-submit', (data) => {
                if (!operateFormRoot.checkValid()) {
                    return;
                }
                let addRuleGraphList = deepClone(vm.ruleGraphStepList);
                addRuleGraphList.push(data);
                addRuleGraphList = this._addListId(addRuleGraphList);

                // 保存后指向最后一个步骤的预览
                let idx = addRuleGraphList.length - 1;

                // 关闭新增步骤弹窗
                vm.addOperateLayer && vm.addOperateLayer.destroy();
                this.changeFeatureStepFlag = true;
                vm._dataPreview(idx, addRuleGraphList, true);
            });

            // 取消收藏 - 隐藏layer
            operateFormRoot.$on('operate-cancel', () => {
                vm.addOperateLayer.destroy();
            });
        },

        // 编辑步骤
        _editOperateStep (e, item, index, aggPreviewFlag) {

            const STEP = 2;

            // //判断当前是否有大于2个步骤（包括数据源第一步），如果大于则字段是倒数第二步的，否则为全部
            let fieldObj = this.ruleGraphList.length > STEP ?
            this.secondLatestDataTypes : Object.assign(this.originalColumnsObj, this.secondLatestDataTypes);
            let fieldList = this.getTransformFieldList(fieldObj);
            this._createOperateLayer(item.type);
            let disabledStatus = index === this.lastGraphIndex ? false : true;
            let dataDetail = {
                formData:deepClone(item.formData),
                fieldList: fieldList,
                timeFieldList: this.originalColumns,
                disabledStatus,
                showOperateBtn:true
            };
            let normalData = this._changeOperateInit(item, dataDetail, fieldList, aggPreviewFlag);
            this.operateLayer.update(normalData).showTo(e.target);
            this.oprIndex = index;
        },
        _createOperateLayer (component) {
            this.operateLayer && this.operateLayer.destroy();
            this.operateLayer = this.$layer(component, {
                anchor: 'right-start',
                cls: 'operate__layer',
                autoHide:true,
                autoMask:false
            });

            let vm = this;
                let operateFormRoot = this.operateLayer.formRoot;

            // 编辑算子成功
            operateFormRoot.$on('operate-submit', (data) => {
                if (!operateFormRoot.isValid()) {
                    return;
                }

                let ruleGraphList = deepClone(vm.ruleGraphList);
                ruleGraphList[vm.oprIndex].params = data;
                vm.operateLayer.destroy();
                vm.operateLayer = null;

                // 编辑的时候不管预览成不成功都要保存到本地。
                this.ruleGraphList = ruleGraphList;
                this.ruleGraphStepList = deepClone(this.ruleGraphList);
                this.changeFeatureStepFlag = true;
                vm._dataPreview(vm.oprIndex, ruleGraphList, true);
            });

            // 取消收藏 - 隐藏layer
            operateFormRoot.$on('operate-cancel', () => {
                vm.operateLayer.destroy();
            });
        },

        // 删除步骤
        _deleteOperate (index) {
            let ruleGraphList = deepClone(this.ruleGraphList);
            ruleGraphList.splice(index, 1);
            this.operateActiveIndex = 0;

            // 取删除后最后的步骤
            this.changeFeatureStepFlag = true;
            this._dataPreview(index - 1, ruleGraphList, true);
        },
        _deleteAllStep () {
            this.$confirm({
                title: _('提示'),
                subTitle: _('确定清空所有特征处理步骤？'),
                msg: _('特征处理步骤尚未保存，清空后将无法找回。'),
                icon: 'warning',
                autoClose: true,
                callback: actionName => {
                    if (actionName === 'submit') {
                        let ruleGraphList = deepClone(this.ruleGraphList).slice(0, 1);
                        this.ruleGraphList = [];
                        this.ruleGraphStepList = [];
                        this.firstColumns = {};

                         // 取删除后最后的步骤
                        this.changeFeatureStepFlag = true;
                        this._dataPreview(0, ruleGraphList, true);
                    }
                }
            });
        },
        handleAIFlowInfo (data) {
            let { graphInfo } = data;
            let dataSourceInfo = parseJson(graphInfo.graphRule)[0];
            if (!dataSourceInfo) {
                return false;
            }
            let tableName = dataSourceInfo.params.index;
            this.dataSourceName = graphInfo.dataSourceName;
            this.dataSourceType = graphInfo.dataSourceType;
             if (graphInfo.dataSourceType === 'csv') {
                 this.dataSourceTitle = `数据集名称：${this.dataSourceName}`;
            } else {
                if (tableName) {
                    this.dataSourceTitle = `数据集名称：${graphInfo.dataSourceName}（${tableName}）`;
                } else {
                    this.dataSourceTitle = `数据集名称：${graphInfo.dataSourceName}`;
                }
            }
            this.ruleGraphList = parseJson(graphInfo.graphRule);

            // 说明肯定存在数据源
            if (this.ruleGraphList.length > 0) {
                this._dataPreview(this.lastGraphIndex, this.ruleGraphList, true);
            }
        },


         // 先预览，预览成功后才能保存
        async _dataPreview (index, list, isSaveInfo = true) {

            // 0代表获取数据源算子
            let stepData = index === 0 ? list : list.slice(0, index + 1);

            // 预览数据源算子所用。
            if (index === 0 && this.ruleGraphList.length > 1) {
                stepData = this.ruleGraphList.slice(0, 1);
            }

            let params = {
                ruleGraph: {
                    operators:stepData,
                    firstColumns:this.firstColumns
                },
                projectId: this.projectId
            };


            let previewURL = urlAdapter('feature/graph/sql');

            // 目的是让整个项目都未遮罩层，因为请求要3-4s才有数据，防止用户点击其它项目产生bug.
            this.$mask();
            if (!this.projectId) {
                return;
            }
            let req;
            this.$once('hook:beforeDestroy', ()=>{
                req.abort();
            });
            let jsonData = await this.$ajax.post(previewURL, params, {
                before:(request)=>{
                    req = request;
                }
            });
            this.$unmask();
            if (!jsonData) {
                return;
            }
            let {success, msg, data} = jsonData;
            if (!success) {
                this.$showErr(msg || _('加载数据失败'));
                return;
            }

            if (index === 0) {

                this.operateActiveIndex = -1;
                this.showDataSourceActive = true;
            } else {
                this.operateActiveIndex = index;
                this.showDataSourceActive = false;
            }
            let columns = data.columns;
            let ruleGraph = data.graphRule.ruleGraph;

             // 读取表格内容
            try {
                if (data.result) {
                    this.previewList = parseJson(data.result);

                    // 获取数据源
                    // this.dataSourceId = parseJson(columnsList[0].jsonValue).dataSourceId;

                    let headerList = [];
                    for (let i in columns) {
                        if (Object.prototype.hasOwnProperty.call(columns, i)) {
                            headerList.push({
                                name: i
                            });
                        }
                    }

                    this.tableHeaderList = headerList;

                    // 如果是操作编辑步骤，则需要更新ruleGraphList和编辑步骤。
                    if (isSaveInfo) {
                        this.ruleGraphList = list;
                        this.lastColumns = columns;
                        this.secondLatestDataTypes = data.secondLatestDataTypes;
                        this.allColumns = data.originalColumnsDataTypes;
                        this.firstColumns = ruleGraph.firstColumns;
                        this.originalColumns = this.getTransformFieldList(data.originalColumnsDataTypes);
                        this.originalColumnsObj = data.originalColumnsDataTypes;

                        // 要存在list时，才保存column
                        this.ruleGraphStepList = deepClone(this.ruleGraphList);
                        let jsons = ruleGraph.operators[0];
                        this.isNullObject(this.aiFlowInfo) ? this.cacheNullAIFlowInfo(jsons) : this.cacheAIFlowInfo();
                    };

                    // 如果有聚合，则取聚合里的分组字段而且需满足预览是有结果的保存才有意义。
                    // let groupByKeys = list.filter(item=>item.operatorType === '06' || item.operatorType === '08');
                    // this.groupByKeys = groupByKeys.length ? parseJson(groupByKeys[0].jsonValue).groupByKeys : [];
                }



            } catch (e) {
                this.previewList = [];
                columns = [];
            };


            // sqlmand保存的是画布最后一个


        },



        // 获取图规则
        async _getAIFlowSnapShot () {

            // let getSnapShotURL = `${FLOW_URL.get}`;
            let getSnapShotURL = `${FLOW_URL.get}/${this.aiFlowId}`;
            this.$mask();
            let jsonData = await this.$ajax.get(getSnapShotURL, {aiFlowId:this.aiFlowId});
            let {success, msg, data} = jsonData;
            this.$unmask();
            if (!success) {
                this.$showErr(msg || _('加载数据失败'));
                return;
            };
            if (!data.graphInfo) {
                this.nullAIInfo = true;
                return false;
            }
            this.saveAiFlowInfo(data);
            this.handleAIFlowInfo(data);
        },



        // 保存步骤
        async _saveGraph () {
            const SAVEURL = `${FLOW_URL.put}/${this.aiFlowId}/snapshot`;
            let params = this.aiFlowInfo;
            if (!this.projectId) {
                return;
            }
            this.$mask();
            let jsonData = await this.$ajax.put(SAVEURL, params);
            let {success, msg} = jsonData;
            this.$unmask();
            if (!success) {
                this.$showErr(msg || _('加载数据失败'));
                return;
            }
        },
        cacheNullAIFlowInfo (jsons) {
           let {params} = jsons;
           let info = {
                aiFlowId:this.aiFlowId,
                projectId:this.projectId,
                dataSourceId:this.dataSourceId,
                graphInfo:{
                    dataSourceId:params.dataSourceId,
                    index:params.index,
                    dataSourceName:this.dataSourceName,
                    dataSourceType:this.dataSourceType,
                    graphRule:JSON.stringify(this.ruleGraphList),
                    lastColumns:JSON.stringify(Object.keys(this.lastColumns)),
                    allColumns: JSON.stringify(Object.keys(this.allColumns)),
                    dagCommand:{
                        operators:this.ruleGraphList
                    }
                },
                trainingInfo:TRAINING_INFO,
                algorithmId:'',
                version:null,
                userId:userID
            };
            info.trainingInfo.dagCommand.operators = this.ruleGraphList;
            this.saveAiFlowInfo(info);
       },
        cacheAIFlowInfo () {
           let info = deepClone(this.aiFlowInfo);
           info.graphInfo.graphRule = JSON.stringify(this.ruleGraphList);
           info.graphInfo.lastColumns = JSON.stringify(Object.keys(this.lastColumns));
           info.graphInfo.allColumns = JSON.stringify(Object.keys(this.allColumns));
           info.graphInfo.dagCommand.operators = this.ruleGraphList;
           info.trainingInfo.dagCommand.operators = this.ruleGraphList;

            // 如果有编辑数据源操作，需要将选择的标签数据和训练数据清空；
           if (this.changeFeatureStepFlag) {
               info.trainingInfo.labelColumns = '[]';
               info.trainingInfo.trainColumns = '[]';
           }
           this.saveAiFlowInfo(info);
        },

        // 预览算子
        queryOperatePreview (index) {
            this._dataPreview(index, this.ruleGraphList, false);
        },

        _timeOperateLayer (formData) {
             let dateTime = {
                start:encodeDate(formData.startDate),
                end:encodeDate(formData.endDate)
            };
            return {
                dateTime
            };
        },

        // 获取运算符号
        _getOperateSign (str) {
            let sign = '';
            if (str.match(/>/)) {
                sign = '>';
            } else if (str.match(/</)) {
                sign = '<';
            } else if (str.match(/!=/)) {
                sign = '!=';
            } else {
                sign = '=';
            }
            return sign;
        },
        _getFilterList (list) {
            let conditions = parseJson(list);
            let filterList = [];
            if (conditions.length > 0) {
                 for (let i = 0; i < conditions.length; i++) {
                    let filterValue = conditions[i].split(/>|<|!=|=/);
                    let filterObj = {
                        name: filterValue[0],
                        opr: this._getOperateSign(conditions[i]),
                        value: filterValue[1]
                    };
                    filterList.push(filterObj);
                }
            } else {
                filterList = [{
                    name:'',
                    opr:'',
                    value:''
                }];
            }
            return filterList;
        },
        _changeOperateInit (item, data, list, editAggFlag) {
            let type = item.sign;
            let operateObj = {};
            let aggregationColumns = {};
            let formData = deepClone(item.formData); ;
            switch (type) {
                case 'sum':
                    formData.columns = parseJson(formData.columns);
                    operateObj.formData = formData;
                break;

                case 'filter':
                    operateObj.filterList = this._getFilterList(formData.conditions);
                break;

                case 'timeFilter':
                    operateObj = this._timeOperateLayer(formData);
                break;

                case 'extractEmail':
                    formData.extractColumn = parseJson(formData.extractColumn);
                    operateObj.formData = formData;
                break;

                case 'concatString':
                {
                    let fieldList = parseJson(formData.fieldList);
                    operateObj = {
                        fieldFirst:fieldList[0],
                        fieldSecond:fieldList[1]
                    };
                    break;
                }

                case 'aggregate':
                    formData.aggregationColumns = parseJson(formData.aggregationColumns);
                    formData.aggregationOps = parseJson(formData.aggregationOps);
                    formData.groupByKeys = parseJson(formData.groupByKeys);
                    operateObj.formData = formData;
                    if (editAggFlag) {
                        aggregationColumns = formData.aggregationColumns;
                        aggregationColumns.forEach(item => {
                            let obj = {
                                value:item,
                                label:item
                            };
                            list.push(obj);
                        });

                        // 数字去重，防止字段重复
                        list = Array.from(new Set(list));
                        operateObj.fieldList = list;
                    }
                    break;

                case 'timeWindowSlideAgg':
                    operateObj = this._timeOperateLayer(formData);
                    formData.aggregationColumns = parseJson(formData.aggregationColumns);
                    formData.aggregationOps = parseJson(formData.aggregationOps);
                    formData.groupByKeys = parseJson(formData.groupByKeys);
                    operateObj.formData = formData;
                    if (editAggFlag) {
                        aggregationColumns = formData.aggregationColumns;
                        aggregationColumns.forEach(item => {
                            let obj = {
                                value:item,
                                label:item
                            };
                            list.push(obj);
                        });

                        // 数字去重，防止字段重复
                        list = Array.from(new Set(list));
                        operateObj.fieldList = list;
                    }
                    break;

                case 'smooth':
                {
                    let factors = [];
                    let factorObj = parseJson(formData.factors);
                    for (let i in factorObj) {
                        if (factorObj.hasOwnProperty(i)) {
                            let obj = {
                                name: i,
                                value:factorObj[i]
                            };
                            factors.push(obj);
                        }
                    }
                    operateObj = {
                        factors
                    };
                    break;
                }

                default:
                    operateObj = {};
                    break;
            }
            return Object.assign(data, operateObj);
        },

        // 特征可视化
        _createFeatureVisualFormWin () {
            if (!this.featureVisualFormWin) {
                this.featureVisualFormWin = this.$modal(FeatureVisual, {
                    autoDestory: true
                });
            }
            return this.featureVisualFormWin;
        },

        _featureVisual () {
            let formWin = this._createFeatureVisualFormWin();
            let fieldObj = this.lastColumns;
            let fieldList = this.getTransformFieldList(fieldObj);
            formWin.open({
                title: '',
                buttons:[{
                    cls: 'btn-default',
                    text: '关闭',
                    actionName: 'cancel'
                }],
                data:{
                    fieldList,
                    previewList:this.previewList,
                    nullCharts:false
                },
                cancel:()=>{
                    formWin.hide();
                }
            });
        }


    },
    destroyed () {
        this.operateLayer && this.operateLayer.destroy();
        this.operateLayer = null;
        this.addOperateLayer && this.addOperateLayer.destroy();
        this.addOperateLayer = null;
    }

};
</script>
<style lang="less">
    .feature-manager-wrap{
        height: 100%;
    }
    .feature-manager-container {
        margin-top: 8px;
       display: flex;
       flex: 1;
        height: calc(100% - 168px);
        overflow: hidden;
       .datasource-container{
            border-bottom: 1px solid #DEE3ED;
            padding:16px 0;
            text-align:center;
            margin: 0 8px;
            .datasource__box{
                background: #FFFFFF;
                border: 1px solid #C8D0E0;
                border-radius: 2px;
                width: 136px;
                height: 32px;
                margin: 0 auto;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                &:hover,&.active{
                    border: 2px solid #3366FF;
                }
            }
        }
       .empty-step__container{
            // height: 612px;
            height: calc(100% - 126px);
            display: flex;
            align-items:center;
            justify-content: center;
            flex-direction:column;
            margin: 0 12px;
            border-bottom: 1px solid #DEE3ED;
            &.has-desource{
                height: calc(100% - 126px);
            }
            .content{
                font-size: 12px;
                color: #5E5E5E;
                margin-top: 4px;
            }
            .empty-img{
                width: 126px;
                height: 248px;
                margin-top: 24px;
            }
       }

    }


    .operate-step__wrap{
        width: 242px;
        background: #fff;
        position: relative;
        height: 100%;
        margin: 0 4px 0 8px;
        border: 1px solid #DEE0E1;
       .operate-step__left{
           display: flex;
           justify-content: space-between;
           align-items:center;
           border-bottom: 1px solid #DEE3ED;
           .btn-blank{
               background: #F5F7FA;
           }
       }
       .operata-btn{
           text-align: center;
            margin-top: 18px;
           .common-btn{
               width: 160px;
               height: 40px;
               margin-bottom: 16px;
               border: none;
           }
           .generate-btn{
                background: #13C2C1;
           }
       }

    }
    .preview-operate{
        margin-right: 10px;
        /* width: 1350px; */
        flex: 1;
        width: calc(100% - 230px);
        border:1px solid #DEE3ED;
        .preview-operate__top-title{
            margin: 0;
            padding-left:12px
        }
        .sfv-toolbar_inner{
            border-bottom: none;
        }
        /* width: calc(100% - 500px); */
    }
</style>
